#pragma once

#include <memory>
#include <sys/epoll.h>

#include "Log.hpp"
#include "InetAddr.hpp"
#include "Socket.hpp"
using namespace socket_ns;
class EpollServer
{
	const static int gnum = 64;

public:
	EpollServer(uint16_t port = 8888)
		: _port(port),
		  _listensock(std::make_unique<TcpSocket>()),
		  _epfd(-1)
	{
		// 1. 创建listensock
		InetAddr addr("0", _port);
		_listensock->BuildListenSocket(addr);

		// 2. 创建epoll模型
		_epfd = ::epoll_create(128); // 放回epoll模型描述符
		if (_epfd < 0)
		{
			LOG(FATAL, "epoll_create error\n");
			exit(5);
		}
		LOG(DEBUG, "epoll_create success, epfd: %d\n", _epfd);
		// 3. 只有一个listensock, listen sock 关心的事件：读事件
		struct epoll_event ev;
		ev.events = EPOLLIN;
		ev.data.fd = _listensock->SockFd(); // 后面使用才能拿得到
		::epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock->SockFd(), &ev);
	}

	~EpollServer()
	{
		_listensock->Close();
		if (_epfd > 0)
		{
			::close(_epfd);
		}
	}

	void handlerEvent(int num)
	{
		for (int i = 0; i < num; i++)
		{
			uint32_t revents = _revs[i].events;
			int sockfd = _revs[i].data.fd;

			// 读事件就绪
			if(revents & EPOLLIN)
			{
				if(sockfd == _listensock->SockFd())	//如果是监听fd
				{
					InetAddr clientaddr;
					int newfd = _listensock->Accepter(&clientaddr);
					if(newfd < 0)
						continue;

					// 获取新链接成功
					//设置进epoll模型
					struct epoll_event ev;
                    ev.events = EPOLLIN;
                    ev.data.fd = newfd;
                    epoll_ctl(_epfd, EPOLL_CTL_ADD, newfd, &ev);
                    LOG(DEBUG, "_listensock ready, accept done, epoll_ctl done, newfd is: %d\n", newfd);
				}
				else	//普通fd
				{
					char buffer[1024];
                    ssize_t n = ::recv(sockfd, buffer, sizeof(buffer), 0); //只能recv一次。不会阻塞
					if (n > 0)
                    {
                        LOG(DEBUG, "normal fd %d ready, recv begin...\n", sockfd);
                        buffer[n] = 0;
                        std::cout << "client say# " << buffer << std::endl;

                        std::string echo_string = "server echo# ";
                        echo_string += buffer;
                        ::send(sockfd, echo_string.c_str(), echo_string.size(), 0);
					}
					else if(n == 0)
					{
						LOG(DEBUG, "normal fd %d close, me too!\n", sockfd);
                        // 对端连接关闭了
                        ::epoll_ctl(_epfd, EPOLL_CTL_DEL, sockfd, nullptr); // 要删除的fd，必须是合法的！
                        ::close(sockfd);	//要先把fd从epoll模型中删除才能关闭
					}
					else
                    {
                        ::epoll_ctl(_epfd, EPOLL_CTL_DEL, sockfd, nullptr); // 要删除的fd，必须是合法的！
                        ::close(sockfd);
                    }
				}
			}
		}
	}

	void loop()
	{
		int timeout = -1; // 多久监视一次	-1是阻塞
		while (true)
		{
			int n = ::epoll_wait(_epfd, _revs, gnum, timeout); // 返回有多少个就绪
			switch (n)
			{
			case 0:
				LOG(DEBUG, "epoll_wait timeout...\n");
				break;
			case -1:
				LOG(DEBUG, "epoll_wait failed...\n");
				break;
			default:
				LOG(DEBUG, "epoll_wait haved event ready..., n : %d\n", n);
				handlerEvent(n); // 就绪队列中是按照就绪先后顺序存的
				break;
			}
		}
	}

private:
	uint16_t _port;
	std::unique_ptr<Socket> _listensock;
	int _epfd; // epoll模型文件描述符

	struct epoll_event _revs[gnum]; // 事件管理
};